﻿using System.Data;
using System.Data.OleDb;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms.DataVisualization.Charting;
using System.Xml;
using System.Xml.Schema;
using uTILLIty.ChartBuilder.Core.Configuration;
using uTILLIty.ChartBuilder.Core.Extensions;
using SerializationFormat = System.Windows.Forms.DataVisualization.Charting.SerializationFormat;

namespace uTILLIty.ChartBuilder.Core
{
	public class ChartBuilder
	{
		private const string DefaultChartAreaName = "ChartArea1";
		private const string DefaultLegendName = "Legend1";
		private static XmlSchemaSet _xmlSchemaSet;

		public void Build(ChartConfiguration config, Stream output)
		{
			var chart = BuildChart(config);
			PrepareChart(config, chart);
			FillFromDataSource(config, chart);
			chart.SaveImage(output, config.Format);
			//chart.Serializer.Content = SerializationContents.Appearance;
			//chart.Serializer.Format = SerializationFormat.Xml;
			//chart.Serializer.IsTemplateMode = true;
			//chart.Serializer.Save("template.xml");
		}

		internal virtual int FillFromDataSource(ChartConfiguration config, Chart chart)
		{
			var table = LoadDataSource(config.DataSource, chart);
			chart.DataSource = table;

			foreach (var series in config.Series)
			{
				//Console.WriteLine(Resources.ProcessingSeries, series.Name);
				CreateSeries(series, chart);
				//chart.DataSource = table;
			}

			chart.DataBind();
			return table.Rows.Count;
		}

		private void ParseAndApplyConfigurationData(XmlNode configurationData, Chart chart)
		{
			using (var reader = new StringReader(configurationData.OuterXml))
			{
				chart.Serializer.IsResetWhenLoading = true;
				chart.Serializer.Content = SerializationContents.All;
				chart.Serializer.Format = SerializationFormat.Xml;
				chart.Serializer.IsTemplateMode = false;
				chart.Serializer.Load(reader);
			}
		}

		protected virtual Chart BuildChart(ChartConfiguration config)
		{
			var chart = new Chart { Width = config.Width, Height = config.Height };
			return chart;
		}

		internal virtual void PrepareChart(ChartConfiguration config, Chart chart)
		{
			if (!chart.ChartAreas.Any(ca => ca.Name == DefaultChartAreaName))
			{
				var chartArea = new ChartArea(DefaultChartAreaName) { Position = { Auto = true }, AlignmentOrientation = AreaAlignmentOrientations.All };
				chart.ChartAreas.Add(chartArea);
			}

			if (!chart.Legends.Any(l => l.Name == DefaultLegendName))
			{
				var legend = new Legend(DefaultLegendName)
											{
												Alignment = StringAlignment.Near,
												DockedToChartArea = DefaultChartAreaName,
												TableStyle = LegendTableStyle.Auto
											};
				chart.Legends.Add(legend);
				legend.Enabled = config.Series.Any(s => !string.IsNullOrWhiteSpace(s.LegendText));
			}

			if (config.Design != null)
				ParseAndApplyConfigurationData(config.Design, chart);
		}

		protected virtual void CreateSeries(SeriesConfiguration series, Chart chart)
		{
			var chartSeries = chart.Series.IndexOf(series.Name) >= 0
				? chart.Series[series.Name] 
				: new Series(series.Name);

			chartSeries.ChartType = series.ChartType;
			chartSeries.XValueMember = series.XValueColumn;
			chartSeries.YValueMembers = series.YValueColumn;
			
			if (!string.IsNullOrWhiteSpace(series.LegendText))
			{
				chartSeries.Legend = DefaultLegendName;
				chartSeries.LegendText = series.LegendText;
				chartSeries.IsVisibleInLegend = true;
			}

			if (!chart.Series.Contains(chartSeries))
				chart.Series.Add(chartSeries);
		}

		internal virtual DataTable LoadDataSource(DataSourceConfiguration dataSource, Chart chart)
		{
			var table = new DataTable(dataSource.Name);

			using (var connection = new OleDbConnection(dataSource.ConnectionString))
			{
				connection.Open();
				var command = connection.CreateCommand();
				command.CommandText = dataSource.SqlCommand;
				var loader = new OleDbDataAdapter(command);
				loader.Fill(table);
				connection.Close();
			}

			//Console.WriteLine(Resources.LoadedSeriesRecords, table.Rows.Count, series.Name);
			return table;
		}

		public static XmlSchemaSet GetSchemas()
		{
			if (_xmlSchemaSet == null)
			{
				var schema = ObjectExtensions.GetSchemaResource<ChartBuilder>("Schema.Configuration.xsd");
				schema.Find<XmlSchemaAny>(true, ele => ele.ProcessContents = XmlSchemaContentProcessing.Skip);
				_xmlSchemaSet = new XmlSchemaSet();
				_xmlSchemaSet.Add(schema);
			}
			return _xmlSchemaSet;
		}
	}
}